//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================

#include <core.h>

//
// ReaderUndoOperation
//
void ReaderUndoOperation::Initialize(Thread *pThread, RWLock *pRWLock)
{
    assert(pThread);
    assert(pRWLock);

    m_pThread = pThread;
    m_pRWLock = pRWLock;
    m_bUndoOwner = TRUE;

    m_pRWLock->m_readerUndoOperationList.InsertLast(this);
}

void ReaderUndoOperation::Undo(Thread *pThread)
{
    bool_t bOriginalPreemption = DzDisablePreemption();

    if (m_bUndoOwner) m_pRWLock->m_nNumberOfActiveReaders--;

    bool_t bWakeUpWriter = (0 == m_pRWLock->m_nNumberOfActiveReaders);
    bool_t bWakeUpReaders = (0 < m_pRWLock->m_uNumberOfPendingReads);

    bool_t bIsOwner = m_pRWLock->m_canWrite.IsOwner(pThread);
    if (bIsOwner) m_pRWLock->m_canWrite.ClearOwner();

    DzRestorePreemption(bOriginalPreemption);

    if (bWakeUpWriter) {
        m_pRWLock->m_canWrite.Pulse(bIsOwner);
    }
    else {
        if (bIsOwner) WaiveInheritedPriority();

        if (bWakeUpReaders) m_pRWLock->m_canRead.PulseAll(); // for downgrade
    }
}

//
// PendingReadUndoOperation
//
class PendingReadUndoOperation : public UndoOperation
{
public:
    PendingReadUndoOperation(RWLock *pRWLock) { m_pRWLock = pRWLock; }

    virtual void Undo(Thread *pThread);

private:
    RWLock  *m_pRWLock;
};

void PendingReadUndoOperation::Undo(Thread *pThread)
{
    bool_t bOriginalPreemption = DzDisablePreemption();
    m_pRWLock->m_uNumberOfPendingReads--;
    DzRestorePreemption(bOriginalPreemption);
}

//
// PendingWriteUndoOperation
//
class PendingWriteUndoOperation : public UndoOperation
{
public:
    PendingWriteUndoOperation(RWLock *pRWLock) { m_pRWLock = pRWLock; }

    virtual void Undo(Thread *pThread);

private:
    RWLock  *m_pRWLock;
};

void PendingWriteUndoOperation::Undo(Thread *pThread)
{
    bool_t bOriginalPreemption = DzDisablePreemption();
    m_pRWLock->m_uNumberOfPendingWrites--;
    DzRestorePreemption(bOriginalPreemption);
}

//
// RWLock
//
DzReaderWriterLock::~DzReaderWriterLock()
{
    bool_t bOriginalPreemption = DzDisablePreemption();

    if (NULL != UndoOperation::m_pNext) {
        UndoOperation::Detach();
    }

    while (!m_readerUndoOperationList.IsEmpty()) {
        register ReaderUndoOperation *pReaderUndoOperation =
                (ReaderUndoOperation *)m_readerUndoOperationList.First();
        pReaderUndoOperation->m_pThread->RemoveReaderUndoOperation(
                    pReaderUndoOperation);
    }

    DzRestorePreemption(bOriginalPreemption);
}

// IMPORTANT: when disable preemption
INLINE void DzReaderWriterLock::AddWriterUndoOperation()
{
    register Thread *pCurrentThread = GetCurrentThread();

    if (NULL != UndoOperation::m_pNext) {
        UndoOperation::Detach();
    }

    m_pUndoOperationOwner = pCurrentThread;
    m_bUndoOwner = TRUE;
    pCurrentThread->AddUndoOperation(this);
}

INLINE void DzReaderWriterLock::RemoveWriterUndoOperation()
{
    register Thread *pCurrentThread = GetCurrentThread();

    bool_t bOriginalPreemption = DzDisablePreemption();
    if (m_pUndoOperationOwner == pCurrentThread) {
        pCurrentThread->RemoveUndoOperation(this);
        m_pUndoOperationOwner = NULL;
    }
    DzRestorePreemption(bOriginalPreemption);
}

void DzReaderWriterLock::IsReaderLockHeld(Boolean *pIsHeld)
{
    assert(pIsHeld);

    GetCurrentThread()->IsReaderLockHeld(this, pIsHeld);
}

void DzReaderWriterLock::IsWriterLockHeld(Boolean *pIsHeld)
{
    assert(pIsHeld);

    GetCurrentThread()->IsWriterLockHeld(this, pIsHeld);
}

void DzReaderWriterLock::ReaderUninterruptibleLock()
{
    PendingReadUndoOperation pendingReadUndoOperation(this);

    m_lock.UninterruptibleLock();

    bool_t bOriginalPreemption = DzDisablePreemption();
    m_uNumberOfPendingReads++;
    GetCurrentThread()->AddUndoOperation(&pendingReadUndoOperation);
    DzRestorePreemption(bOriginalPreemption);

    if (0 < m_uNumberOfPendingWrites) {
        m_canRead.UninterruptibleWait(&m_lock);
    }

    while (0 > m_nNumberOfActiveReaders) {
        m_canRead.UninterruptibleWait(&m_lock);
    }

    bOriginalPreemption = DzDisablePreemption();
    if (0 == m_nNumberOfActiveReaders) m_canWrite.SetOwner(GetCurrentThread());
    m_nNumberOfActiveReaders++;
    GetCurrentThread()->AddReaderUndoOperation(this);
    DzRestorePreemption(bOriginalPreemption);

    bOriginalPreemption = DzDisablePreemption();
    GetCurrentThread()->RemoveUndoOperation(&pendingReadUndoOperation);
    m_uNumberOfPendingReads--;
    DzRestorePreemption(bOriginalPreemption);

    m_lock.Unlock();
}

void DzReaderWriterLock::ReaderLock(WaitResult *pResult)
{
    assert(pResult);

    *pResult = WaitResult_OK;

    PendingReadUndoOperation pendingReadUndoOperation(this);

    m_lock.UninterruptibleLock();

    bool_t bOriginalPreemption = DzDisablePreemption();
    m_uNumberOfPendingReads++;
    GetCurrentThread()->AddUndoOperation(&pendingReadUndoOperation);
    DzRestorePreemption(bOriginalPreemption);

    if (0 < m_uNumberOfPendingWrites) {
        m_canRead.Wait(&m_lock, pResult);
        if (WaitResult_OK != *pResult) goto Exit;
    }

    while (0 > m_nNumberOfActiveReaders) {
        m_canRead.Wait(&m_lock, pResult);
        if (WaitResult_OK != *pResult) goto Exit;
    }

    bOriginalPreemption = DzDisablePreemption();
    if (0 == m_nNumberOfActiveReaders) m_canWrite.SetOwner(GetCurrentThread());
    m_nNumberOfActiveReaders++;
    GetCurrentThread()->AddReaderUndoOperation(this);
    DzRestorePreemption(bOriginalPreemption);

Exit:
    bOriginalPreemption = DzDisablePreemption();
    GetCurrentThread()->RemoveUndoOperation(&pendingReadUndoOperation);
    m_uNumberOfPendingReads--;
    DzRestorePreemption(bOriginalPreemption);

    m_lock.Unlock();
}

void DzReaderWriterLock::ReaderTryUninterruptibleLock(
    uint_t ticks, WaitResult *pResult)
{
    assert(pResult);

    *pResult = WaitResult_OK;

    PendingReadUndoOperation pendingReadUndoOperation(this);

    m_lock.UninterruptibleLock();

    if (0 == ticks) {
        if (0 < m_uNumberOfPendingWrites || 0 > m_nNumberOfActiveReaders) {
            m_lock.Unlock();
            *pResult = WaitResult_TimedOut;
            return;
        }
    }

    bool_t bOriginalPreemption = DzDisablePreemption();
    m_uNumberOfPendingReads++;
    GetCurrentThread()->AddUndoOperation(&pendingReadUndoOperation);
    DzRestorePreemption(bOriginalPreemption);

    if (0 < m_uNumberOfPendingWrites) {
        m_canRead.TryUninterruptibleWait(&m_lock, ticks, &ticks, pResult);
        if (WaitResult_OK != *pResult) goto Exit;
    }

    while (0 > m_nNumberOfActiveReaders) {
        if (0 == ticks) {
            *pResult = WaitResult_TimedOut;
            goto Exit;
        }
        m_canRead.TryUninterruptibleWait(&m_lock, ticks, &ticks, pResult);
        if (WaitResult_OK != *pResult) goto Exit;
    }

    bOriginalPreemption = DzDisablePreemption();
    if (0 == m_nNumberOfActiveReaders) m_canWrite.SetOwner(GetCurrentThread());
    m_nNumberOfActiveReaders++;
    GetCurrentThread()->AddReaderUndoOperation(this);
    DzRestorePreemption(bOriginalPreemption);

Exit:
    bOriginalPreemption = DzDisablePreemption();
    GetCurrentThread()->RemoveUndoOperation(&pendingReadUndoOperation);
    m_uNumberOfPendingReads--;
    DzRestorePreemption(bOriginalPreemption);

    m_lock.Unlock();
}

void DzReaderWriterLock::ReaderTryLock(uint_t ticks, WaitResult *pResult)
{
    assert(pResult);

    *pResult = WaitResult_OK;

    PendingReadUndoOperation pendingReadUndoOperation(this);

    m_lock.UninterruptibleLock();

    if (0 == ticks) {
        if (0 < m_uNumberOfPendingWrites || 0 > m_nNumberOfActiveReaders) {
            m_lock.Unlock();
            *pResult = WaitResult_TimedOut;
            return;
        }
    }

    bool_t bOriginalPreemption = DzDisablePreemption();
    m_uNumberOfPendingReads++;
    GetCurrentThread()->AddUndoOperation(&pendingReadUndoOperation);
    DzRestorePreemption(bOriginalPreemption);

    if (0 < m_uNumberOfPendingWrites) {
        m_canRead.TryWait(&m_lock, ticks, &ticks, pResult);
        if (WaitResult_OK != *pResult) goto Exit;
    }

    while (0 > m_nNumberOfActiveReaders) {
        if (0 == ticks) {
            *pResult = WaitResult_TimedOut;
            goto Exit;
        }
        m_canRead.TryWait(&m_lock, ticks, &ticks, pResult);
        if (WaitResult_OK != *pResult) goto Exit;
    }

    bOriginalPreemption = DzDisablePreemption();
    if (0 == m_nNumberOfActiveReaders) m_canWrite.SetOwner(GetCurrentThread());
    m_nNumberOfActiveReaders++;
    GetCurrentThread()->AddReaderUndoOperation(this);
    DzRestorePreemption(bOriginalPreemption);

Exit:
    bOriginalPreemption = DzDisablePreemption();
    GetCurrentThread()->RemoveUndoOperation(&pendingReadUndoOperation);
    m_uNumberOfPendingReads--;
    DzRestorePreemption(bOriginalPreemption);

    m_lock.Unlock();
}

void DzReaderWriterLock::ReaderUnlock()
{
    ReaderUndoOperation *pReaderUndoOperation =
        GetCurrentThread()->FindReaderUndoOperation(this);
    assert(pReaderUndoOperation);

    m_lock.UninterruptibleLock();

    bool_t bOriginalPreemption = DzDisablePreemption();
    pReaderUndoOperation->m_bUndoOwner = FALSE;
    m_nNumberOfActiveReaders--;
    DzRestorePreemption(bOriginalPreemption);

    bool_t bWakeUpWriter = (0 == m_nNumberOfActiveReaders);

    bOriginalPreemption = DzDisablePreemption();
    bool_t bIsOwner = m_canWrite.IsOwner(GetCurrentThread());
    if (bIsOwner) m_canWrite.ClearOwner();
    DzRestorePreemption(bOriginalPreemption);

    m_lock.Unlock();

    if (bWakeUpWriter)
        m_canWrite.Pulse(bIsOwner);
    else if (bIsOwner)
        WaiveInheritedPriority();

    GetCurrentThread()->RemoveReaderUndoOperation(pReaderUndoOperation);
}

void DzReaderWriterLock::WriterUninterruptibleLock()
{
    PendingWriteUndoOperation pendingWriteUndoOperation(this);

    m_lock.UninterruptibleLock();

    bool_t bOriginalPreemption = DzDisablePreemption();
    m_uNumberOfPendingWrites++;
    GetCurrentThread()->AddUndoOperation(&pendingWriteUndoOperation);
    DzRestorePreemption(bOriginalPreemption);

    while (0 != m_nNumberOfActiveReaders) {
        m_canWrite.UninterruptibleWait(&m_lock);
    }

    bOriginalPreemption = DzDisablePreemption();
    m_canRead.SetOwner(GetCurrentThread());
    m_canWrite.SetOwner(GetCurrentThread());
    m_nNumberOfActiveReaders = -1;
    AddWriterUndoOperation();
    DzRestorePreemption(bOriginalPreemption);

    bOriginalPreemption = DzDisablePreemption();
    GetCurrentThread()->RemoveUndoOperation(&pendingWriteUndoOperation);
    m_uNumberOfPendingWrites--;
    DzRestorePreemption(bOriginalPreemption);

    m_lock.Unlock();
}

void DzReaderWriterLock::WriterLock(WaitResult *pResult)
{
    assert(pResult);

    *pResult = WaitResult_OK;

    PendingWriteUndoOperation pendingWriteUndoOperation(this);

    m_lock.UninterruptibleLock();

    bool_t bOriginalPreemption = DzDisablePreemption();
    m_uNumberOfPendingWrites++;
    GetCurrentThread()->AddUndoOperation(&pendingWriteUndoOperation);
    DzRestorePreemption(bOriginalPreemption);

    while (0 != m_nNumberOfActiveReaders) {
        m_canWrite.Wait(&m_lock, pResult);
        if (WaitResult_OK != *pResult) goto Exit;
    }

    bOriginalPreemption = DzDisablePreemption();
    m_canRead.SetOwner(GetCurrentThread());
    m_canWrite.SetOwner(GetCurrentThread());
    m_nNumberOfActiveReaders = -1;
    AddWriterUndoOperation();
    DzRestorePreemption(bOriginalPreemption);

Exit:
    bOriginalPreemption = DzDisablePreemption();
    GetCurrentThread()->RemoveUndoOperation(&pendingWriteUndoOperation);
    m_uNumberOfPendingWrites--;
    DzRestorePreemption(bOriginalPreemption);

    if (WaitResult_OK == *pResult) {
        m_lock.Unlock();
    }
    else {
        bool_t bWakeUpReaders = (0 < m_uNumberOfPendingReads);

        m_lock.Unlock();

        if (bWakeUpReaders) m_canRead.PulseAll(FALSE);
    }
}

void DzReaderWriterLock::WriterTryUninterruptibleLock(
    uint_t ticks, WaitResult *pResult)
{
    assert(pResult);

    *pResult = WaitResult_OK;

    PendingWriteUndoOperation pendingWriteUndoOperation(this);

    m_lock.UninterruptibleLock();

    if (0 == ticks) {
        if (0 != m_nNumberOfActiveReaders) {
            m_lock.Unlock();
            *pResult = WaitResult_TimedOut;
            return;
        }
    }

    bool_t bOriginalPreemption = DzDisablePreemption();
    m_uNumberOfPendingWrites++;
    GetCurrentThread()->AddUndoOperation(&pendingWriteUndoOperation);
    DzRestorePreemption(bOriginalPreemption);

    while (0 != m_nNumberOfActiveReaders) {
        if (0 == ticks) {
            *pResult = WaitResult_TimedOut;
            goto Exit;
        }
        m_canWrite.TryUninterruptibleWait(&m_lock, ticks, &ticks, pResult);
        if (WaitResult_OK != *pResult) goto Exit;
    }

    bOriginalPreemption = DzDisablePreemption();
    m_canRead.SetOwner(GetCurrentThread());
    m_canWrite.SetOwner(GetCurrentThread());
    m_nNumberOfActiveReaders = -1;
    AddWriterUndoOperation();
    DzRestorePreemption(bOriginalPreemption);

Exit:
    bOriginalPreemption = DzDisablePreemption();
    GetCurrentThread()->RemoveUndoOperation(&pendingWriteUndoOperation);
    m_uNumberOfPendingWrites--;
    DzRestorePreemption(bOriginalPreemption);

    if (WaitResult_OK == *pResult) {
        m_lock.Unlock();
    }
    else {
        bool_t bWakeUpReaders = (0 < m_uNumberOfPendingReads);

        m_lock.Unlock();

        if (bWakeUpReaders) m_canRead.PulseAll(FALSE);
    }
}

void DzReaderWriterLock::WriterTryLock(uint_t ticks, WaitResult *pResult)
{
    assert(pResult);

    *pResult = WaitResult_OK;

    PendingWriteUndoOperation pendingWriteUndoOperation(this);

    m_lock.UninterruptibleLock();

    if (0 == ticks) {
        if (0 != m_nNumberOfActiveReaders) {
            m_lock.Unlock();
            *pResult = WaitResult_TimedOut;
            return;
        }
    }

    bool_t bOriginalPreemption = DzDisablePreemption();
    m_uNumberOfPendingWrites++;
    GetCurrentThread()->AddUndoOperation(&pendingWriteUndoOperation);
    DzRestorePreemption(bOriginalPreemption);

    while (0 != m_nNumberOfActiveReaders) {
        if (0 == ticks) {
            *pResult = WaitResult_TimedOut;
            goto Exit;
        }

        m_canWrite.TryWait(&m_lock, ticks, &ticks, pResult);
        if (WaitResult_OK != *pResult) goto Exit;
    }

    bOriginalPreemption = DzDisablePreemption();
    m_canRead.SetOwner(GetCurrentThread());
    m_canWrite.SetOwner(GetCurrentThread());
    m_nNumberOfActiveReaders = -1;
    AddWriterUndoOperation();
    DzRestorePreemption(bOriginalPreemption);

Exit:
    bOriginalPreemption = DzDisablePreemption();
    GetCurrentThread()->RemoveUndoOperation(&pendingWriteUndoOperation);
    m_uNumberOfPendingWrites--;
    DzRestorePreemption(bOriginalPreemption);

    if (WaitResult_OK == *pResult) {
        m_lock.Unlock();
    }
    else {
        bool_t bWakeUpReaders = (0 < m_uNumberOfPendingReads);

        m_lock.Unlock();

        if (bWakeUpReaders) m_canRead.PulseAll(FALSE);
    }
}

void DzReaderWriterLock::WriterUnlock()
{
    m_lock.UninterruptibleLock();

    bool_t bOriginalPreemption = DzDisablePreemption();
    m_bUndoOwner = FALSE;
    m_nNumberOfActiveReaders = 0;
    DzRestorePreemption(bOriginalPreemption);

    bool_t bWakeUpReaders = (0 < m_uNumberOfPendingReads);

    bOriginalPreemption = DzDisablePreemption();
    m_canRead.ClearOwner();
    m_canWrite.ClearOwner();
    DzRestorePreemption(bOriginalPreemption);

    m_lock.Unlock();

    if (bWakeUpReaders)
        m_canRead.PulseAll(TRUE);
    else
        m_canWrite.Pulse(TRUE);

    RemoveWriterUndoOperation();
}

void DzReaderWriterLock::Undo(Thread *pThread)
{
    if (m_pUndoOperationOwner != pThread) return;

    bool_t bOriginalPreemption = DzDisablePreemption();

    if (m_bUndoOwner) m_nNumberOfActiveReaders = 0;

    bool_t bWakeUpReaders = (0 < m_uNumberOfPendingReads);

    if (m_canRead.HasOwner())   m_canRead.ClearOwner();
    if (m_canWrite.HasOwner())  m_canWrite.ClearOwner();

    DzRestorePreemption(bOriginalPreemption);

    if (bWakeUpReaders)
        m_canRead.PulseAll(TRUE);
    else
        WaiveInheritedPriority();
}

void DzReaderWriterLock::UninterruptibleUpgradeToWriter()
{
    ReaderUndoOperation *pReaderUndoOperation =
        GetCurrentThread()->FindReaderUndoOperation(this);
    assert(pReaderUndoOperation);

    m_lock.UninterruptibleLock();

    if (1 == m_nNumberOfActiveReaders) {
        bool_t bOriginalPreemption = DzDisablePreemption();
        GetCurrentThread()->RemoveReaderUndoOperation(pReaderUndoOperation);
        if (!m_canWrite.HasOwner()) m_canWrite.SetOwner(GetCurrentThread());
        m_canRead.SetOwner(GetCurrentThread());
        m_nNumberOfActiveReaders = -1;
        AddWriterUndoOperation();
        DzRestorePreemption(bOriginalPreemption);
    }
    else {
        PendingWriteUndoOperation pendingWriteUndoOperation(this);

        bool_t bOriginalPreemption = DzDisablePreemption();
        m_uNumberOfPendingWrites++;
        GetCurrentThread()->AddUndoOperation(&pendingWriteUndoOperation);
        DzRestorePreemption(bOriginalPreemption);

        bOriginalPreemption = DzDisablePreemption();
        GetCurrentThread()->RemoveReaderUndoOperation(pReaderUndoOperation);
        m_nNumberOfActiveReaders--;
        bool_t bIsOwner = m_canWrite.IsOwner(GetCurrentThread());
        if (bIsOwner) m_canWrite.ClearOwner();
        DzRestorePreemption(bOriginalPreemption);

        if (bIsOwner) WaiveInheritedPriority();

        while (0 != m_nNumberOfActiveReaders) {
            m_canWrite.UninterruptibleWait(&m_lock);
        }

        bOriginalPreemption = DzDisablePreemption();
        m_canRead.SetOwner(GetCurrentThread());
        m_canWrite.SetOwner(GetCurrentThread());
        m_nNumberOfActiveReaders = -1;
        AddWriterUndoOperation();
        DzRestorePreemption(bOriginalPreemption);

        bOriginalPreemption = DzDisablePreemption();
        GetCurrentThread()->RemoveUndoOperation(&pendingWriteUndoOperation);
        m_uNumberOfPendingWrites--;
        DzRestorePreemption(bOriginalPreemption);
    }

    m_lock.Unlock();
}

void DzReaderWriterLock::UpgradeToWriter(WaitResult *pResult)
{
    assert(pResult);

    *pResult = WaitResult_OK;

    ReaderUndoOperation *pReaderUndoOperation =
        GetCurrentThread()->FindReaderUndoOperation(this);
    assert(pReaderUndoOperation);

    m_lock.UninterruptibleLock();

    if (1 == m_nNumberOfActiveReaders) {
        bool_t bOriginalPreemption = DzDisablePreemption();
        GetCurrentThread()->RemoveReaderUndoOperation(pReaderUndoOperation);
        if (!m_canWrite.HasOwner()) m_canWrite.SetOwner(GetCurrentThread());
        m_canRead.SetOwner(GetCurrentThread());
        m_nNumberOfActiveReaders = -1;
        AddWriterUndoOperation();
        DzRestorePreemption(bOriginalPreemption);
    }
    else {
        PendingWriteUndoOperation pendingWriteUndoOperation(this);

        bool_t bOriginalPreemption = DzDisablePreemption();
        m_uNumberOfPendingWrites++;
        GetCurrentThread()->AddUndoOperation(&pendingWriteUndoOperation);
        DzRestorePreemption(bOriginalPreemption);

        bOriginalPreemption = DzDisablePreemption();
        GetCurrentThread()->RemoveReaderUndoOperation(pReaderUndoOperation);
        m_nNumberOfActiveReaders--;
        bool_t bIsOwner = m_canWrite.IsOwner(GetCurrentThread());
        if (bIsOwner) m_canWrite.ClearOwner();
        DzRestorePreemption(bOriginalPreemption);

        if (bIsOwner) WaiveInheritedPriority();

        while (0 != m_nNumberOfActiveReaders) {
            m_canWrite.Wait(&m_lock, pResult);
            if (WaitResult_OK != *pResult) goto Exit;
        }

        bOriginalPreemption = DzDisablePreemption();
        m_canRead.SetOwner(GetCurrentThread());
        m_canWrite.SetOwner(GetCurrentThread());
        m_nNumberOfActiveReaders = -1;
        AddWriterUndoOperation();
        DzRestorePreemption(bOriginalPreemption);

Exit:
        bOriginalPreemption = DzDisablePreemption();
        GetCurrentThread()->RemoveUndoOperation(&pendingWriteUndoOperation);
        m_uNumberOfPendingWrites--;
        DzRestorePreemption(bOriginalPreemption);
    }

    if (WaitResult_OK == *pResult) {
        m_lock.Unlock();
    }
    else {
        bool_t bWakeUpReaders = (0 < m_uNumberOfPendingReads);

        m_lock.Unlock();

        if (bWakeUpReaders) m_canRead.PulseAll(FALSE);

        ReaderUninterruptibleLock();
    }
}

void DzReaderWriterLock::TryUninterruptibleUpgradeToWriter(
    uint_t ticks, WaitResult *pResult)
{
    assert(pResult);

    *pResult = WaitResult_OK;

    ReaderUndoOperation *pReaderUndoOperation =
        GetCurrentThread()->FindReaderUndoOperation(this);
    assert(pReaderUndoOperation);

    m_lock.UninterruptibleLock();

    if (1 == m_nNumberOfActiveReaders) {
        bool_t bOriginalPreemption = DzDisablePreemption();
        GetCurrentThread()->RemoveReaderUndoOperation(pReaderUndoOperation);
        if (!m_canWrite.HasOwner()) m_canWrite.SetOwner(GetCurrentThread());
        m_canRead.SetOwner(GetCurrentThread());
        m_nNumberOfActiveReaders = -1;
        AddWriterUndoOperation();
        DzRestorePreemption(bOriginalPreemption);
    }
    else {
        if (0 == ticks) {
            m_lock.Unlock();
            *pResult = WaitResult_TimedOut;
            return;
        }

        PendingWriteUndoOperation pendingWriteUndoOperation(this);

        bool_t bOriginalPreemption = DzDisablePreemption();
        m_uNumberOfPendingWrites++;
        GetCurrentThread()->AddUndoOperation(&pendingWriteUndoOperation);
        DzRestorePreemption(bOriginalPreemption);

        bOriginalPreemption = DzDisablePreemption();
        GetCurrentThread()->RemoveReaderUndoOperation(pReaderUndoOperation);
        m_nNumberOfActiveReaders--;
        bool_t bIsOwner = m_canWrite.IsOwner(GetCurrentThread());
        if (bIsOwner) m_canWrite.ClearOwner();
        DzRestorePreemption(bOriginalPreemption);

        if (bIsOwner) WaiveInheritedPriority();

        while (0 != m_nNumberOfActiveReaders) {
            if (0 == ticks) {
                *pResult = WaitResult_TimedOut;
                goto Exit;
            }

            m_canWrite.TryUninterruptibleWait(&m_lock, ticks, &ticks, pResult);
            if (WaitResult_OK != *pResult) goto Exit;
        }

        bOriginalPreemption = DzDisablePreemption();
        m_canRead.SetOwner(GetCurrentThread());
        m_canWrite.SetOwner(GetCurrentThread());
        m_nNumberOfActiveReaders = -1;
        AddWriterUndoOperation();
        DzRestorePreemption(bOriginalPreemption);

Exit:
        bOriginalPreemption = DzDisablePreemption();
        GetCurrentThread()->RemoveUndoOperation(&pendingWriteUndoOperation);
        m_uNumberOfPendingWrites--;
        DzRestorePreemption(bOriginalPreemption);
    }

    if (WaitResult_OK == *pResult) {
        m_lock.Unlock();
    }
    else {
        bool_t bWakeUpReaders = (0 < m_uNumberOfPendingReads);

        m_lock.Unlock();

        if (bWakeUpReaders) m_canRead.PulseAll(FALSE);

        ReaderUninterruptibleLock();
    }
}

void DzReaderWriterLock::TryUpgradeToWriter(uint_t ticks, WaitResult *pResult)
{
    assert(pResult);

    *pResult = WaitResult_OK;

    ReaderUndoOperation *pReaderUndoOperation =
        GetCurrentThread()->FindReaderUndoOperation(this);
    assert(pReaderUndoOperation);

    m_lock.UninterruptibleLock();

    if (1 == m_nNumberOfActiveReaders) {
        bool_t bOriginalPreemption = DzDisablePreemption();
        GetCurrentThread()->RemoveReaderUndoOperation(pReaderUndoOperation);
        if (!m_canWrite.HasOwner()) m_canWrite.SetOwner(GetCurrentThread());
        m_canRead.SetOwner(GetCurrentThread());
        m_nNumberOfActiveReaders = -1;
        AddWriterUndoOperation();
        DzRestorePreemption(bOriginalPreemption);
    }
    else {
        if (0 == ticks) {
            m_lock.Unlock();
            *pResult = WaitResult_TimedOut;
            return;
        }

        PendingWriteUndoOperation pendingWriteUndoOperation(this);

        bool_t bOriginalPreemption = DzDisablePreemption();
        m_uNumberOfPendingWrites++;
        GetCurrentThread()->AddUndoOperation(&pendingWriteUndoOperation);
        DzRestorePreemption(bOriginalPreemption);

        bOriginalPreemption = DzDisablePreemption();
        GetCurrentThread()->RemoveReaderUndoOperation(pReaderUndoOperation);
        m_nNumberOfActiveReaders--;
        bool_t bIsOwner = m_canWrite.IsOwner(GetCurrentThread());
        if (bIsOwner) m_canWrite.ClearOwner();
        DzRestorePreemption(bOriginalPreemption);

        if (bIsOwner) WaiveInheritedPriority();

        while (0 != m_nNumberOfActiveReaders) {
            if (0 == ticks) {
                *pResult = WaitResult_TimedOut;
                goto Exit;
            }
            m_canWrite.TryWait(&m_lock, ticks, &ticks, pResult);
            if (WaitResult_OK != *pResult) goto Exit;
        }

        bOriginalPreemption = DzDisablePreemption();
        m_canRead.SetOwner(GetCurrentThread());
        m_canWrite.SetOwner(GetCurrentThread());
        m_nNumberOfActiveReaders = -1;
        AddWriterUndoOperation();
        DzRestorePreemption(bOriginalPreemption);

Exit:
        bOriginalPreemption = DzDisablePreemption();
        GetCurrentThread()->RemoveUndoOperation(&pendingWriteUndoOperation);
        m_uNumberOfPendingWrites--;
        DzRestorePreemption(bOriginalPreemption);
    }

    if (WaitResult_OK == *pResult) {
        m_lock.Unlock();
    }
    else {
        bool_t bWakeUpReaders = (0 < m_uNumberOfPendingReads);

        m_lock.Unlock();

        if (bWakeUpReaders) m_canRead.PulseAll(FALSE);

        ReaderUninterruptibleLock();
    }
}

void DzReaderWriterLock::DowngradeFromWriter()
{
    m_lock.UninterruptibleLock();

    bool_t bOriginalPreemption = DzDisablePreemption();
    RemoveWriterUndoOperation();
    m_nNumberOfActiveReaders = 1;
    GetCurrentThread()->AddReaderUndoOperation(this);
    DzRestorePreemption(bOriginalPreemption);

    bool_t bWakeUpReaders = (0 < m_uNumberOfPendingReads);

    m_lock.Unlock();

    bOriginalPreemption = DzDisablePreemption();
    m_canRead.ClearOwner();
    DzRestorePreemption(bOriginalPreemption);

    if (bWakeUpReaders)
        m_canRead.PulseAll(TRUE);
    else
        WaiveInheritedPriority();
}
